#pragma once

#include <assert.h>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <type_traits>

namespace good_cptl
{

template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
bool checked_add(T *result, T a, T b)
{
  if (std::is_signed_v<T>) {
    if (a >= 0) {
      if (std::numeric_limits<T>::max() - a < b) {
        *result = {};
        return false;
      }
    } else if (b < std::numeric_limits<T>::min() - a) {
      *result = {};
      return false;
    }
    *result = a + b;
    return true;
  } else {
    if (a <= std::numeric_limits<T>::max() - b) {
      *result = a + b;
      return true;
    } else {
      return false;
    }
  }
}

template <typename T, typename = std::enable_if_t<std::is_unsigned<T>::value>>
bool checked_mul(T* result, T a, T b) {
  assert(sizeof(T) < sizeof(uint64_t));
  uint64_t res = static_cast<uint64_t>(a) * static_cast<uint64_t>(b);
  const uint64_t overflowMask = ~((1ULL << (sizeof(T) * 8)) - 1);
  if ((res & overflowMask) != 0) {
    *result = {};
    return false;
  }
  *result = static_cast<T>(res);
  return true;
}





}